package level.editor.control.collision;

import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics2D;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.awt.geom.Ellipse2D;
import java.awt.image.BufferedImage;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;

import level.editor.control.Handler;
import level.editor.control.layer.LayerPainter;
import level.editor.control.main.Main;
import level.editor.control.tile.TileConstants;
import level.model.Addable;

public class CollisionHandler extends Handler implements ActionListener,
		WindowFocusListener
{
	public CollisionHandler(CollisionControl control)
	{
		super();
		this.control = control;
		type = TYPE_LINE;
		buffer = null;
		isRect = false;

		prepareIcon();
		prepareIconRect();

		control.addKeyListener(this);
	}

	@Override
	public void finish()
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void mousePressed(MouseEvent e)
	{
		if (SwingUtilities.isLeftMouseButton(e)
				&& e.getSource() instanceof Main)
		{
			Main main = (Main) e.getSource();
			main.requestFocusInWindow();
			if (!main.getCollisionLayer().isLocked()
					&& main.getCollisionLayer().isVisible()
					&& type == TYPE_LINE)
			{
				if (buffer == null)
				{
					// Change to custom cursor here
					main.setCursor(new Cursor(Cursor.MOVE_CURSOR));
					Point point = snapPoint(main.getOrigin(), e.getPoint());
					CollisionLayer layer = main.getCollisionLayer();
					buffer = newCollisionBuffer(layer.getLayerNumber(), point,
							main, false);
					main.minimapUpdate();
				}
			}
			else if (!main.getCollisionLayer().isLocked()
					&& main.getCollisionLayer().isVisible()
					&& type == TYPE_RECTANGLE)
			{
				if (buffer == null)
				{
					Point point = regularSnapPoint(main.getOrigin(),
							e.getPoint());
					CollisionLayer layer = main.getCollisionLayer();
					buffer = newCollisionBuffer(layer.getLayerNumber(), point,
							main, true);
					main.minimapUpdate();
					// layer.setGhost(null, null);
					layer.repaint();
					isRect = true;
				}
			}
		}
	}

	@Override
	public void mouseDragged(MouseEvent e)
	{
		if (SwingUtilities.isLeftMouseButton(e)
				&& e.getSource() instanceof Main)
		{
			Main main = (Main) e.getSource();
			main.requestFocusInWindow();
			if (!main.getCollisionLayer().isLocked()
					&& main.getCollisionLayer().isVisible()
					&& type == TYPE_LINE)
			{
				if (buffer != null)
				{
					CollisionLayer layer = main.getCollisionLayer();
					buffer.repaint();
					buffer.setEndPoint(noSnapPoint(main.getOrigin(),
							e.getPoint()));
					Point point = snapPoint(main.getOrigin(), e.getPoint());
					CollisionDot dot = new CollisionDot(point);
					layer.setGhost(dot, icon);
					layer.repaint();
				}
			}
			else if (!main.getCollisionLayer().isLocked()
					&& main.getCollisionLayer().isVisible()
					&& type == TYPE_RECTANGLE)
			{
				if (buffer != null)
				{
					buffer.repaint();
					buffer.setEndPoint(regularSnapPoint(main.getOrigin(),
							e.getPoint()));
				}
			}
		}
	}

	@Override
	public void mouseReleased(MouseEvent e)
	{
		if (SwingUtilities.isLeftMouseButton(e)
				&& e.getSource() instanceof Main)
		{
			Main main = (Main) e.getSource();
			main.requestFocusInWindow();
			main.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
			if (!main.getCollisionLayer().isLocked()
					&& main.getCollisionLayer().isVisible()
					&& type == TYPE_LINE)
			{
				if (buffer != null)
				{
					CollisionLayer layer = main.getCollisionLayer();
					buffer.setEndPoint(snapPoint(main.getOrigin(), e.getPoint()));
					buffer.finish(layer);
					main.minimapUpdate();
					buffer.repaint();
					buffer = null;
				}
			}
			else if (!main.getCollisionLayer().isLocked()
					&& main.getCollisionLayer().isVisible()
					&& type == TYPE_RECTANGLE)
			{
				if (buffer != null && isRect)
				{
					CollisionLayer layer = main.getCollisionLayer();
					buffer.setEndPoint(regularSnapPoint(main.getOrigin(),
							e.getPoint()));
					buffer.finish(layer);
					main.minimapUpdate();
					buffer.repaint();
					isRect = false;
					buffer = null;
					type = TYPE_LINE;
				}
			}
		}
	}

	// Implement ghost circles here

	@Override
	public void mouseMoved(MouseEvent e)
	{
		if (e.getSource() instanceof Main)
		{
			Main main = (Main) e.getSource();
			main.requestFocusInWindow();
			if (main.getCollisionLayer() != null)
			{
				if (!main.getCollisionLayer().isLocked()
						&& main.getCollisionLayer().isVisible()
						&& type == TYPE_LINE)
				{
					if (buffer == null)
					{
						CollisionLayer layer = main.getCollisionLayer();
						Point point = snapPoint(main.getOrigin(), e.getPoint());
						CollisionDot dot = new CollisionDot(point);
						layer.setGhost(dot, icon);
						layer.repaint();
					}
				}

				if (!main.getCollisionLayer().isLocked()
						&& main.getCollisionLayer().isVisible()
						&& type == TYPE_RECTANGLE)
				{
					if (buffer == null)
					{
						CollisionLayer layer = main.getCollisionLayer();
						Point point = regularSnapPoint(main.getOrigin(),
								e.getPoint());
						CollisionDot dot = new CollisionDot(point);
						point.x -= TileConstants.TILE_SIZE;
						point.y -= TileConstants.TILE_SIZE;
						layer.setGhost(dot, iconRect);
						layer.repaint();
					}
				}
			}
		}
	}

	@Override
	public void keyPressed(KeyEvent e)
	{

		if (e.getKeyCode() == KeyEvent.VK_CONTROL)
		{
			Main main = (Main) control.getMain();
			if (main.getCollisionLayer() != null)
			{
				if (buffer == null)
				{
					type = TYPE_RECTANGLE;
					CollisionLayer layer = main.getCollisionLayer();
					Point p = MouseInfo.getPointerInfo().getLocation();
					SwingUtilities.convertPointFromScreen(p, main);
					Point point = regularSnapPoint(main.getOrigin(), p);
					point.x -= TileConstants.TILE_SIZE;
					point.y -= TileConstants.TILE_SIZE;
					CollisionDot dot = new CollisionDot(point);
					layer.setGhost(dot, iconRect);
					layer.repaint();
					main.repaint();
				}
			}
		}
	}

	@Override
	public void keyReleased(KeyEvent e)
	{
		if (e.getKeyCode() == KeyEvent.VK_CONTROL)
		{
			if (!isRect)
				type = TYPE_LINE;

			if (isRect)
				buffer.repaint();

			Main main = (Main) control.getMain();
			CollisionLayer layer = main.getCollisionLayer();

			if (layer != null)
			{
				if (buffer == null)
				{
					type = TYPE_LINE;
					Point p = MouseInfo.getPointerInfo().getLocation();
					SwingUtilities.convertPointFromScreen(p, main);
					Point point = snapPoint(main.getOrigin(), p);
					CollisionDot dot = new CollisionDot(point);
					layer.setGhost(dot, icon);
				}
				layer.repaint();
				main.repaint();
			}
		}
	}

	@Override
	public void windowGainedFocus(WindowEvent arg0)
	{
		// TODO Auto-generated method stub

	}

	@Override
	public void windowLostFocus(WindowEvent arg0)
	{
		type = TYPE_LINE;
		if (control.getMain() != null)
			control.getMain().repaint();
	}

	@Override
	public void paint(Graphics2D g, Point topLeft)
	{
		String typeString = "Type:  " + type;
		g.drawString(typeString, (int) topLeft.getX(), (int) topLeft.getY());
	}

	@Override
	public void actionPerformed(ActionEvent e)
	{
		control.registerHandler();
		control.requestFocusInWindow();
		control.getMain().repaint();
	}

	public class CollisionDot implements Addable
	{
		public CollisionDot(Point point)
		{
			this.point = point;
		}

		public Point getPoint()
		{
			return point;
		}

		private Point point;

		@Override
		public boolean equals(Addable addable)
		{
			CollisionDot dot = (CollisionDot) addable;
			if (dot.getPoint().x == point.x && dot.getPoint().y == point.y)
				return true;
			else
				return false;
		}

		@Override
		public Addable cloneAddable()
		{
			return new CollisionDot(point);
		}
	}

	private void prepareIcon()
	{
		BufferedImage bufferedImage = new BufferedImage(LayerPainter.SIZE,
				LayerPainter.SIZE, BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = bufferedImage.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g.setColor(Color.YELLOW);
		g.setStroke(LayerPainter.STROKE);
		Ellipse2D ellipse = new Ellipse2D.Float(0, 0, LayerPainter.SIZE,
				LayerPainter.SIZE);
		g.fill(ellipse);
		g.dispose();

		icon = new ImageIcon(bufferedImage);
	}

	private void prepareIconRect()
	{
		BufferedImage bufferedImage = new BufferedImage(
				3 * TileConstants.TILE_SIZE, 3 * TileConstants.TILE_SIZE,
				BufferedImage.TYPE_INT_ARGB);
		Graphics2D g = bufferedImage.createGraphics();
		g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g.setStroke(LayerPainter.STROKE);
		g.setColor(Color.YELLOW);

		Rectangle rect = new Rectangle(TileConstants.TILE_SIZE
				+ LayerPainter.DOT_OFFSET, TileConstants.TILE_SIZE
				+ LayerPainter.DOT_OFFSET, TileConstants.TILE_SIZE,
				TileConstants.TILE_SIZE);
		g.draw(rect);

		Ellipse2D ellipse = new Ellipse2D.Float(TileConstants.TILE_SIZE,
				TileConstants.TILE_SIZE, LayerPainter.SIZE, LayerPainter.SIZE);
		g.fill(ellipse);

		ellipse = new Ellipse2D.Float(2 * TileConstants.TILE_SIZE,
				TileConstants.TILE_SIZE, LayerPainter.SIZE, LayerPainter.SIZE);
		g.fill(ellipse);

		ellipse = new Ellipse2D.Float(TileConstants.TILE_SIZE,
				2 * TileConstants.TILE_SIZE, LayerPainter.SIZE,
				LayerPainter.SIZE);
		g.fill(ellipse);

		ellipse = new Ellipse2D.Float(2 * TileConstants.TILE_SIZE,
				2 * TileConstants.TILE_SIZE, LayerPainter.SIZE,
				LayerPainter.SIZE);
		g.fill(ellipse);
		g.dispose();

		iconRect = new ImageIcon(bufferedImage);
	}

	private Point snapPoint(Point origin, Point initialPoint)
	{
		Point point = new Point();

		initialPoint.x += TileConstants.TILE_SIZE / 2;
		initialPoint.y += TileConstants.TILE_SIZE / 2;
		point.x = (int) (initialPoint.getX() - origin.getX());
		point.y = (int) (initialPoint.getY() - origin.getY());

		point.x = point.x - (point.x < 0 ? TileConstants.TILE_SIZE : 0);
		point.y = point.y - (point.y < 0 ? TileConstants.TILE_SIZE : 0);

		point.x = point.x - point.x % TileConstants.TILE_SIZE;
		point.y = point.y - point.y % TileConstants.TILE_SIZE;

		return point;
	}

	private Point regularSnapPoint(Point origin, Point initialPoint)
	{
		Point point = new Point();
		point.x = (int) (initialPoint.getX() - origin.getX());
		point.y = (int) (initialPoint.getY() - origin.getY());

		point.x = point.x - (point.x < 0 ? TileConstants.TILE_SIZE : 0);
		point.y = point.y - (point.y < 0 ? TileConstants.TILE_SIZE : 0);

		point.x = point.x - point.x % TileConstants.TILE_SIZE;
		point.y = point.y - point.y % TileConstants.TILE_SIZE;

		return point;
	}

	private Point noSnapPoint(Point origin, Point initialPoint)
	{
		Point point = new Point();

		point.x = (int) (initialPoint.getX() - origin.getX());
		point.y = (int) (initialPoint.getY() - origin.getY());

		return point;
	}

	private CollisionBuffer newCollisionBuffer(Integer layerNumber,
			Point point, Main main, boolean isRect)
	{
		CollisionBuffer buffer = new CollisionBuffer(layerNumber, point, main,
				isRect);
		return buffer;
	}

	private String type;
	private CollisionControl control;
	private CollisionBuffer buffer;
	private boolean isRect;
	private Icon icon;
	private Icon iconRect;
	private static final String TYPE_LINE = "Line";
	private static final String TYPE_RECTANGLE = "Drag rectangle";
}
